from django.utils.translation import gettext as _
from rest_framework import exceptions as drf_exceptions


class Error(Exception):
    default_message = 'We encountered an internal error. Please try again.'
    default_code = 'InternalError'
    default_status_code = 500

    def __init__(self, message: str = '', code: str = '', status_code=None, extend_msg=''):
        """
        :param message: 错误描述
        :param code: 错误代码
        :param status_code: HTTP状态码
        :param extend_msg: 扩展错误描述的信息，追加到message后面
        """
        self.message = message if message else self.default_message
        self.code = code if code else self.default_code
        self.status_code = self.default_status_code if status_code is None else status_code
        if extend_msg:
            self.message += '&&' + extend_msg

    def __repr__(self):
        return f'{type(self)}(message={self.message}, code={self.code}, status_code={self.status_code})'

    def __str__(self):
        return self.message

    def detail_str(self):
        return self.__repr__()

    def err_data(self):
        return {
            'code': self.code,
            'message': self.message
        }

    @classmethod
    def from_error(cls, err):
        if isinstance(err, Error):
            return cls(message=err.message, code=err.code, status_code=err.status_code)

        return cls(message=str(err))


class APIException(Error):
    pass


class BadRequest(APIException):
    default_message = 'BadRequest.'
    default_code = 'BadRequest'
    default_status_code = 400


class InvalidArgument(BadRequest):
    default_message = 'Invalid Argument.'
    default_code = 'InvalidArgument'


class ParameterConflict(BadRequest):
    default_message = _('参数冲突')
    default_code = 'ParameterConflict'


class NoFoundArgument(BadRequest):
    default_message = 'No Found Argument.'
    default_code = 'NoFoundArgument'


class ValidationError(BadRequest):
    default_message = 'Validation Error.'
    default_code = 'ValidationError'


class AuthenticationFailed(APIException):
    default_message = 'Incorrect authentication credentials.'
    default_code = 'AuthenticationFailed'
    default_status_code = 401


class NotAuthenticated(APIException):
    default_message = 'Authentication credentials were not provided.'
    default_code = 'NotAuthenticated'
    default_status_code = 401


class InvalidJWT(APIException):
    default_message = 'Token is invalid or expired.'
    default_code = 'InvalidJWT'
    default_status_code = 401


class AccessDenied(APIException):
    default_message = 'Access Denied.'
    default_code = 'AccessDenied'
    default_status_code = 403


class NotFound(APIException):
    default_message = 'Not Found.'
    default_code = 'NotFound'
    default_status_code = 404


class UserNotExist(NotFound):
    default_message = _('用户不存在')
    default_code = 'UserNotExist'


class ConflictError(APIException):
    default_message = '由于和被请求的资源的当前状态之间存在冲突，请求无法完成'
    default_code = 'Conflict'
    default_status_code = 409


def convert_to_error(err):
    if isinstance(err, Error):
        return err

    if not isinstance(err, drf_exceptions.APIException):
        return APIException.from_error(err)

    message = str(err)
    if isinstance(err, drf_exceptions.NotFound):
        return NotFound(message=message)
    if isinstance(err, drf_exceptions.PermissionDenied):
        return AccessDenied(message=message)
    if isinstance(err, drf_exceptions.NotAuthenticated):
        return AuthenticationFailed(message=message)

    return APIException.from_error(err)
